控制器是 MVC 架構的一部分。它們是從 yii\base\Controller 擴展而來的類別的物件,負責處理請求和產生回應。特別是,在從 應用程式 接管控制權後,控制器將分析傳入的請求資料,將其傳遞給 模型,將模型結果注入 視圖,最後產生傳出的回應。
控制器由動作組成,動作是最終使用者可以定址和請求執行的最基本單元。一個控制器可以有一個或多個動作。
以下範例顯示了一個具有兩個動作的 post
控制器:view
和 create
namespace app\controllers;
use Yii;
use app\models\Post;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
class PostController extends Controller
{
public function actionView($id)
{
$model = Post::findOne($id);
if ($model === null) {
throw new NotFoundHttpException;
}
return $this->render('view', [
'model' => $model,
]);
}
public function actionCreate()
{
$model = new Post;
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
}
在 view
動作(由 actionView()
方法定義)中,程式碼首先根據請求的模型 ID 載入 模型;如果模型載入成功,它將使用名為 view
的 視圖 顯示它。否則,它將拋出例外。
在 create
動作(由 actionCreate()
方法定義)中,程式碼類似。它首先嘗試使用請求資料填充 模型 的新實例並儲存模型。如果兩者都成功,它將將瀏覽器重新導向到具有新建立模型 ID 的 view
動作。否則,它將顯示 create
視圖,使用者可以透過該視圖提供所需的輸入。
最終使用者透過所謂的路由來定址動作。路由是一個字串,由以下部分組成
路由採用以下格式
ControllerID/ActionID
如果控制器屬於模組,則採用以下格式
ModuleID/ControllerID/ActionID
因此,如果使用者使用 URL https://hostname/index.php?r=site/index
請求,則將執行 site
控制器中的 index
動作。有關路由如何解析為動作的更多詳細資訊,請參閱 路由與 URL 建立 章節。
在 Web 應用程式 中,控制器應從 yii\web\Controller 或其子類別擴展。同樣,在 主控台應用程式 中,控制器應從 yii\console\Controller 或其子類別擴展。以下程式碼定義了一個 site
控制器
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
}
通常,控制器旨在處理關於特定類型資源的請求。因此,控制器 ID 通常是名詞,指的是它們正在處理的資源類型。例如,您可以將 article
用作處理文章資料的控制器的 ID。
預設情況下,控制器 ID 應僅包含以下字元:小寫英文字母、數字、底線、連字號和正斜線。例如,article
和 post-comment
都是有效的控制器 ID,而 article?
、PostComment
、admin\post
則不是。
控制器 ID 也可能包含子目錄前綴。例如,admin/article
代表 控制器命名空間 下 admin
子目錄中的 article
控制器。子目錄前綴的有效字元包括:大小寫英文字母、數字、底線和正斜線,其中正斜線用作多層子目錄的分隔符(例如 panels/admin
)。
控制器類別名稱可以根據以下程序從控制器 ID 派生
Controller
。以下是一些範例,假設 控制器命名空間 採用預設值 app\controllers
article
變成 app\controllers\ArticleController
;post-comment
變成 app\controllers\PostCommentController
;admin/post-comment
變成 app\controllers\admin\PostCommentController
;adminPanels/post-comment
變成 app\controllers\adminPanels\PostCommentController
。控制器類別必須是 可自動載入的。因此,在上述範例中,article
控制器類別應儲存在其 別名 為 @app/controllers/ArticleController.php
的檔案中;而 admin/post-comment
控制器應位於 @app/controllers/admin/PostCommentController.php
中。
資訊:最後一個範例
admin/post-comment
顯示了如何將控制器放在 控制器命名空間 的子目錄下。當您想要將控制器組織成多個類別,並且不想使用 模組 時,這很有用。
您可以設定 控制器地圖,以克服上述控制器 ID 和類別名稱的限制。當您使用第三方控制器並且無法控制其類別名稱時,這主要很有用。
[
'controllerMap' => [
// declares "account" controller using a class name
'account' => 'app\controllers\UserController',
// declares "article" controller using a configuration array
'article' => [
'class' => 'app\controllers\PostController',
'enableCsrfValidation' => false,
],
],
]
每個應用程式都有一個預設控制器,透過 yii\base\Application::$defaultRoute 屬性指定。當請求未指定 路由 時,將使用此屬性指定的路由。對於 Web 應用程式,其值為 'site'
,而對於 主控台應用程式,其值為 help
。因此,如果 URL 是 https://hostname/index.php
,則 site
控制器將處理請求。
您可以使用以下 應用程式設定 變更預設控制器
[
'defaultRoute' => 'main',
]
建立動作可以像在控制器類別中定義所謂的動作方法一樣簡單。動作方法是一個public 方法,其名稱以單字 action
開頭。動作方法的傳回值表示要傳送給最終使用者的回應資料。以下程式碼定義了兩個動作,index
和 hello-world
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public function actionIndex()
{
return $this->render('index');
}
public function actionHelloWorld()
{
return 'Hello World';
}
}
動作通常旨在執行對資源的特定操作。因此,動作 ID 通常是動詞,例如 view
、update
等。
預設情況下,動作 ID 應僅包含以下字元:小寫英文字母、數字、底線和連字號(您可以使用連字號分隔單字)。例如,view
、update2
和 comment-post
都是有效的動作 ID,而 view?
和 Update
則不是。
您可以使用兩種方式建立動作:內聯動作和獨立動作。內聯動作定義為控制器類別中的方法,而獨立動作是一個擴展 yii\base\Action 或其子類別的類別。內聯動作的建立工作量較少,如果您不打算重複使用這些動作,通常是首選。另一方面,建立獨立動作主要是為了在不同的控制器中使用或作為 擴充套件 重新發布。
內聯動作是指以動作方法定義的動作,正如我們剛才描述的那樣。
動作方法的名稱是根據以下程序從動作 ID 派生的
action
。例如,index
變成 actionIndex
,而 hello-world
變成 actionHelloWorld
。
注意:動作方法的名稱是區分大小寫的。如果您有一個名為
ActionIndex
的方法,它將不會被視為動作方法,因此,對index
動作的請求將導致例外。另請注意,動作方法必須是 public 的。private 或 protected 方法不會定義內聯動作。
內聯動作是最常定義的動作,因為它們的建立工作量很少。但是,如果您計劃在不同位置重複使用相同的動作,或者如果您想要重新發布動作,則應考慮將其定義為獨立動作。
獨立動作是根據擴展 yii\base\Action 或其子類別的動作類別定義的。例如,在 Yii 發行版中,有 yii\web\ViewAction 和 yii\web\ErrorAction,它們都是獨立動作。
若要使用獨立動作,您應該透過覆寫控制器類別中的 yii\base\Controller::actions() 方法,在動作地圖中宣告它,如下所示
public function actions()
{
return [
// declares "error" action using a class name
'error' => 'yii\web\ErrorAction',
// declares "view" action using a configuration array
'view' => [
'class' => 'yii\web\ViewAction',
'viewPrefix' => '',
],
];
}
如您所見,actions()
方法應傳回一個陣列,其鍵是動作 ID,值是對應的動作類別名稱或 設定。與內聯動作不同,獨立動作的動作 ID 可以包含任意字元,只要它們在 actions()
方法中宣告即可。
若要建立獨立動作類別,您應該擴展 yii\base\Action 或子類別,並實作一個名為 run()
的 public 方法。run()
方法的角色類似於動作方法。例如,
<?php
namespace app\components;
use yii\base\Action;
class HelloWorldAction extends Action
{
public function run()
{
return "Hello World";
}
}
動作方法或獨立動作的 run()
方法的傳回值非常重要。它代表對應動作的結果。
傳回值可以是 回應 物件,該物件將作為回應傳送給最終使用者。
在上面顯示的範例中,動作結果都是字串,這些字串將被視為要傳送給最終使用者的回應主體。以下範例顯示了動作如何透過傳回回應物件將使用者瀏覽器重新導向到新的 URL(因為 redirect() 方法傳回回應物件)
public function actionForward()
{
// redirect the user browser to https://example.com
return $this->redirect('https://example.com');
}
內聯動作的動作方法和獨立動作的 run()
方法可以採用參數,稱為動作參數。它們的值是從請求中取得的。對於 Web 應用程式,每個動作參數的值都是使用參數名稱作為鍵從 $_GET
中檢索的;對於 主控台應用程式,它們對應於命令列引數。
在以下範例中,view
動作(內聯動作)宣告了兩個參數:$id
和 $version
。
namespace app\controllers;
use yii\web\Controller;
class PostController extends Controller
{
public function actionView($id, $version = null)
{
// ...
}
}
對於不同的請求,動作參數將按如下方式填充
https://hostname/index.php?r=post/view&id=123
:$id
參數將填充值 '123'
,而 $version
仍然是 null
,因為沒有 version
查詢參數。https://hostname/index.php?r=post/view&id=123&version=2
:$id
和 $version
參數將分別填充 '123'
和 '2'
。https://hostname/index.php?r=post/view
:將拋出 yii\web\BadRequestHttpException 例外,因為請求中未提供必要的 $id
參數。https://hostname/index.php?r=post/view&id[]=123
:將拋出 yii\web\BadRequestHttpException 例外,因為 $id
參數正在接收意外的陣列值 ['123']
。如果您希望動作參數接受陣列值,則應使用 array
類型提示它,如下所示
public function actionView(array $id, $version = null)
{
// ...
}
現在,如果請求是 https://hostname/index.php?r=post/view&id[]=123
,則 $id
參數將採用 ['123']
的值。如果請求是 https://hostname/index.php?r=post/view&id=123
,則 $id
參數仍將收到相同的陣列值,因為純量值 '123'
將自動轉換為陣列。
以上範例主要顯示了動作參數如何適用於 Web 應用程式。對於主控台應用程式,請參閱 命令列指令 章節以取得更多詳細資訊。
每個控制器都有一個預設動作,透過 yii\base\Controller::$defaultAction 屬性指定。當 路由 僅包含控制器 ID 時,這表示請求的是指定控制器的預設動作。
預設情況下,預設動作設定為 index
。如果您想要變更預設值,只需在控制器類別中覆寫此屬性,如下所示
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public $defaultAction = 'home';
public function actionHome()
{
return $this->render('home');
}
}
在處理請求時,應用程式 將根據請求的 路由 建立控制器。然後,控制器將經歷以下生命週期以完成請求
beforeAction()
方法。false
,則將跳過其餘未呼叫的 beforeAction()
方法,並且將取消動作執行。beforeAction()
方法呼叫都會觸發 beforeAction
事件,您可以將處理常式附加到該事件。afterAction()
方法。afterAction()
方法呼叫都會觸發 afterAction
事件,您可以將處理常式附加到該事件。在設計良好的應用程式中,控制器通常非常精簡,每個動作僅包含幾行程式碼。如果您的控制器相當複雜,通常表示您應該重構它並將一些程式碼移至其他類別。
以下是一些具體的最佳實踐。控制器
發現錯字或您認為此頁面需要改進?
在 github 上編輯 !
註冊 或 登入 以發表評論。